#include "symboltable.h"
#include "exceptions.h"
#include "codegenerator.h"

#include <stdio.h>
#include <sstream>
#include <algorithm>

#include "../Common/include/symboltable.h"

using std::map;
using namespace Thinder;

Thinder::SymbolTable symboltable;
int identCounter = 0, blockCounter = 0;
 
void processDeclarations(ParseTree::Name ns, vector<ParseTree::Declaration> &decls);
void processDeclaration(ParseTree::Name ns, ParseTree::Declaration &decl);
void processStatement(ParseTree::Name ns, ParseTree::Statement &stat);
void processStatement(bool freeBlockNS, ParseTree::Name ns, ParseTree::Statement &stat);
void processClassDeclarations(ParseTree::Name ns, vector<ParseTree::ClassDeclaration> &decls);
void processClassDeclaration(ParseTree::Name ns, ParseTree::ClassDeclaration &decl);

string printName(ParseTree::Name name)
{
    string s = "";
    for(unsigned int i = 0; i < name.size(); i++)
        s += name[i].identifier + string(".");
    return s.substr(0, s.length() - 1);
}

string printType(ParseTree::Type type)
{
    string str;
    
    if (type.typeType == ParseTree::Type::PrimitiveType)
    {
        switch (type.primitiveType)
        {
            case ParseTree::Type::Int:
                str += "int";
                break;
            case ParseTree::Type::Float:
                str += "float";
                break;
            case ParseTree::Type::Bool:
                str += "bool";
                break;
            case ParseTree::Type::Char:
                str += "char";
                break;
            case ParseTree::Type::Byte:
                str += "byte";
                break;
            case ParseTree::Type::Short:
                str += "short";
                break;
            case ParseTree::Type::Long:
                str += "long";
                break;
            case ParseTree::Type::Double:
                str += "double";
                break;
        }
    } else if (type.typeType == ParseTree::Type::UserType)
    {
        str += printName(type.userType);
    }
    
    return str;
}

string printSymbol(Symbol s)
{
    string str = "(";
    str += "internal: " + s.internal;
    str += ", symbolType: ";
    if (s.symbolType == Symbol::Var)
        str += "Var";
    if (s.symbolType == Symbol::Func)
        str += "Func";
    if (s.symbolType == Symbol::Class)
        str += "Class";
    if (s.symbolType == Symbol::Operator)
        str += "Operator";
    if (s.symbolType == Symbol::Constructor)
        str += "Constructor";
    if (s.symbolType == Symbol::Link)
        str += "Link";
    str += ", type: ";
    str += printType(s.type);
    
    
    return str + ")";
}

string getNewBlockReference()
{
    char buffer [64];
    sprintf(buffer, "%x", blockCounter++);
    return string("tb_") + buffer;
}

string getNewIdentifier()
{
    char buffer [64];
    sprintf(buffer, "%x", identCounter++);
    return string("ti_") + buffer;
}

string getNewIdentifier(string &id)
{
    char buffer [64];
    sprintf(buffer, "%x", identCounter++);
    return string("ti_") + id + "_" + buffer;
}

map<ParseTree::Name, Symbol> buildSymbolTable(ParseTree::Thinder &thinder)
{
    processDeclarations(thinder.ns, thinder.declarations);
    
    return symboltable;
}

string printSymbolTable(Thinder::SymbolTable s)
{
    string ret;
    Thinder::SymbolTable::iterator it;
    ret = "Symbol table contains:\n";
    for (it = s.begin() ; it != s.end(); it++)
        ret += printName((*it).first) + " => " + printSymbol((*it).second) + "\n";
    return ret;
}

void processDeclarations(ParseTree::Name ns, vector<ParseTree::Declaration> &decls)
{
    for(unsigned int i = 0; i < decls.size(); i++)
        processDeclaration(ns, decls[i]);
}

void processDeclaration(ParseTree::Name ns, ParseTree::Declaration &decl)
{
    // TODO: fail if there already exist a symbol with an exact name
    if (decl.declType == ParseTree::Declaration::VarDecl)
    {
        for (unsigned int i = 0; i < decl.vars.size(); i++)
        {
            Symbol s;
            s.type = decl.type;
            s.symbolType = Symbol::Var;
            s.modifiers = decl.modifiers;
            s.internal = getNewIdentifier(decl.vars[i].identifier.identifier);
            ParseTree::Name name = ns;
            name.push_back(decl.vars[i].identifier);
            symboltable[name] = s;
        }
    } else if (decl.declType == ParseTree::Declaration::FunctionDecl)
    {
        Symbol s;
        s.type = decl.type;
        s.symbolType = Symbol::Func;
        s.modifiers = decl.modifiers;
        s.internal = getNewIdentifier(decl.identifier.identifier);
        ParseTree::Name funcname = ns;
        funcname.push_back(decl.identifier);
        
        if (symboltable.find(funcname) != symboltable.end())
        {
            // function already exist, this is an overload
            int i = 0;
            std::stringstream out;
            out << i;
            funcname.push_back(ParseTree::Identifier("%" + out.str()));
            while (symboltable.find(funcname) != symboltable.end())
            {
                ++i;
                funcname.pop_back();
                std::stringstream out;
                out << i;
                funcname.push_back(ParseTree::Identifier(string("%") + out.str()));
            }
        }
        
        // FormPars
        for (unsigned int i = 0; i < decl.formPars.size(); i++)
        {
            Symbol sp;
            sp.type = decl.formPars[i].type;
            sp.symbolType = Symbol::Var;
            sp.internal = getNewIdentifier(decl.formPars[i].identifier.identifier);
            ParseTree::Name name = funcname;
            name.push_back(decl.formPars[i].identifier);
            symboltable[name] = sp;
            s.argumentTypes.push_back(sp.type);
        }
        
        symboltable[funcname] = s;
       
        processStatement(true, funcname, decl.statement);
    } else if (decl.declType == ParseTree::Declaration::ClassDecl)
    {
        Symbol s;
        s.symbolType = Symbol::Class;
        s.modifiers = decl.modifiers;
        s.internal = getNewIdentifier(decl.identifier.identifier);
        ParseTree::Type type;
        ParseTree::Name classname = ns;
        classname.push_back(decl.identifier);
        type.typeType = ParseTree::Type::UserType;
        type.userType = classname;
        s.type = type;
        symboltable[classname] = s;
        
        processClassDeclarations(classname, decl.classdecls);
    }
}

void processClassDeclarations(ParseTree::Name ns, vector<ParseTree::ClassDeclaration> &decls)
{
    for(unsigned int i = 0; i < decls.size(); i++)
        processClassDeclaration(ns, decls[i]);
}

void processClassDeclaration(ParseTree::Name ns, ParseTree::ClassDeclaration &decl)
{
    if (decl.declType == ParseTree::ClassDeclaration::FieldMeth)
    {
        processDeclaration(ns, *decl.declaration);
    } else if (decl.declType == ParseTree::ClassDeclaration::Constructor)
    {
        Symbol s;
        s.symbolType = Symbol::Constructor;
        s.modifiers = decl.modifiers;
        s.internal = getNewIdentifier(decl.identifier.identifier);
        ParseTree::Name funcname = ns;
        funcname.push_back(decl.identifier);
        if (symboltable.find(funcname) != symboltable.end())
        {
            // function already exist, this is an overload
            int i = 0;
            std::stringstream out;
            out << i;
            funcname.push_back(ParseTree::Identifier("%" + out.str()));
            while (symboltable.find(funcname) != symboltable.end())
            {
                ++i;
                funcname.pop_back();
                std::stringstream out;
                out << i;
                funcname.push_back(ParseTree::Identifier(string("%") + out.str()));
            }
        }
        ParseTree::Type type;
        type.typeType = ParseTree::Type::UserType;
        type.userType = ns;
        //std::cout << "constructor type: " << printName(ns) << std::endl;
        s.type = type;
        
        // FormPars
        for (unsigned int i = 0; i < decl.formPars.size(); i++)
        {
            //std::cout << "    formpar type: " << printType(decl.formPars[i].type) << std::endl;
            Symbol sp;
            sp.type = decl.formPars[i].type;
            sp.symbolType = Symbol::Var;
            sp.internal = getNewIdentifier(decl.formPars[i].identifier.identifier);
            ParseTree::Name name = funcname;
            name.push_back(decl.formPars[i].identifier);
            symboltable[name] = sp;
            s.argumentTypes.push_back(sp.type);
        }
        
        symboltable[funcname] = s;
        
        // TODO: aparte error checking
        if (ns.back().identifier != decl.identifier.identifier)
        {
            TException up(6, "A constructor of class \"" + printName(ns) + "\" must be named \"" + ns.back().identifier + "\" (instead of \"" + decl.identifier.identifier + "\")");
            throw up;
        }
        
        // FormPars
        for (unsigned int i = 0; i < decl.formPars.size(); i++)
        {
            Symbol s;
            s.type = decl.formPars[i].type;
            s.symbolType = Symbol::Var;
            s.internal = getNewIdentifier(decl.formPars[i].identifier.identifier);
            ParseTree::Name name = funcname;
            name.push_back(decl.formPars[i].identifier);
            symboltable[name] = s;
        }
        
        processStatement(true, funcname, decl.statement);
    } 
}

void processStatement(ParseTree::Name ns, ParseTree::Statement &stat)
{
    processStatement(false, ns, stat);
}

// freeBlockNS should be true when a possible block is part of of the container namespace, e.g. a function.
void processStatement(bool freeBlockNS, ParseTree::Name ns, ParseTree::Statement &stat)
{
    if (stat.statementType == ParseTree::Statement::Block)
    {
        ParseTree::Name name = ns;
        if (!freeBlockNS)
        {
            stat.internalBlockReference = getNewBlockReference();
            name.push_back(ParseTree::Identifier(stat.internalBlockReference));
        }
        for (unsigned int i = 0; i < stat.block.size(); i++)
        {
            processStatement(name, stat.block[i]);
        }
    } else if (stat.statementType == ParseTree::Statement::Declaration)
    {
        processDeclaration(ns, *stat.decl);
    }
}

void split(vector<string> & theStringVector, const string &theString, const string &theDelimiter)
{
    size_t  start = 0, end = 0;

    while ( end != string::npos )
    {   
        end = theString.find( theDelimiter, start );
        // If at end, use length=maxLength.  Else use length=end-start.
        theStringVector.push_back( theString.substr( start, (end == string::npos) ? string::npos : end - start ) );

      // If at end, use start=maxSize.  Else use start=end+delimiter.
    start = (   ( end > (string::npos - theDelimiter.size()) )
              ?  string::npos  :  end + theDelimiter.size()    );
  }
}

bool isDigit(char c)
{
    return (c >= '0' && c <= '9');    
}

int stringToInt(string in)
{
    string is = "";
    char c = in.at(0);
    while (isDigit(c)) 
    {
        is.append(1, c);
        in.erase(0, 1);
        if (in.length() != 0)
            c = in.at(0);
        else
            c = 'a';
    }
    if (is == "0")
        return 0;
    int i = atoi(is.c_str());
    if (i == 0)
    {
        TException up(3, "Expected integer token not found!");
        throw up;
    }
    return i;
}

ParseTree::Type nameToType(ParseTree::Name name)
{
    ParseTree::Type type;
    if (name.size() == 1 && name[0].identifier == "int")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Int;
    } else if (name.size() == 1 && name[0].identifier == "bool")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Bool;
    } else if (name.size() == 1 && name[0].identifier == "byte")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Byte;
    } else if (name.size() == 1 && name[0].identifier == "float")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Float;
    } else if (name.size() == 1 && name[0].identifier == "long")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Long;
    } else if (name.size() == 1 && name[0].identifier == "short")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Short;
    } else if (name.size() == 1 && name[0].identifier == "char")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Char;
    } else if (name.size() == 1 && name[0].identifier == "double")
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        type.primitiveType = ParseTree::Type::Double;
    } else
    {
        type.typeType = ParseTree::Type::UserType;
        type.userType = name;
    } 
    
    return type;
}


Thinder::SymbolTable parseSymbolTable(string input)
{
    Thinder::SymbolTable table;
    vector<string> lines;
    split(lines, input, "\n");
    
    for (unsigned int i = 0; i < lines.size(); i++)
    {
        Symbol symbol;
        vector<string> tokens;
        split(tokens, lines[i], "\t");
        if (tokens.size() != 5)
            continue;
        
        string stype = tokens[0];
        int modifiers = stringToInt(tokens[1]);
        string internal = tokens[2];
        ParseTree::Name type = toName(tokens[3]);
        string namestring = tokens[4];
        
        if (stype == "0")
            symbol.symbolType = Symbol::Var;
        if (stype == "1")
            symbol.symbolType = Symbol::Func;
        if (stype == "2")
            symbol.symbolType = Symbol::Class;
        if (stype == "3")
            symbol.symbolType = Symbol::Constructor;
        if (stype == "4")
            symbol.symbolType = Symbol::Operator;
        if (stype == "5")
            symbol.symbolType = Symbol::Link;
        
        if (modifiers & FLAG_PUBLIC)
            symbol.modifiers.push_back(ParseTree::Public);
        if (modifiers & FLAG_PROTECTED)
            symbol.modifiers.push_back(ParseTree::Protected);
        if (modifiers & FLAG_PRIVATE)
            symbol.modifiers.push_back(ParseTree::Private);
        if (modifiers & FLAG_STATIC)
            symbol.modifiers.push_back(ParseTree::Static);
        if (modifiers & FLAG_ABSTRACT)
            symbol.modifiers.push_back(ParseTree::Abstract);
        if (modifiers & FLAG_FINAL)
            symbol.modifiers.push_back(ParseTree::Final);
        
        symbol.internal = internal;
        
        symbol.type = nameToType(type);
        
        ParseTree::Name name;
        if (symbol.symbolType == Symbol::Func || symbol.symbolType == Symbol::Operator || symbol.symbolType == Symbol::Constructor)
        {
            name = toName(namestring.substr(0, namestring.find("(")));
            namestring = namestring.substr(namestring.find("(") + 1);
            namestring = namestring.substr(0, namestring.find(")"));
            vector<string> args;
            split(args, namestring, ",");
            for (unsigned int j = 0; j < args.size(); j++)
            {
                symbol.argumentTypes.push_back(nameToType(toName(args[j])));
            }
        } else {
            name = toName(namestring);
        }  
        
        table[name] = symbol;
    }

    return table;
}

// Merges symboltable s2 into s1
void mergeSymbolTable(Thinder::SymbolTable &s1, Thinder::SymbolTable &s2)
{
    Thinder::SymbolTable::iterator it;
    for (it = s2.begin() ; it != s2.end(); it++)
        s1[(*it).first] = (*it).second;
}

void addSymlink(Thinder::SymbolTable &s, ParseTree::Name target, ParseTree::Name link_name)
{
    Symbol symbol;
    symbol.symbolType = Symbol::Link;
    symbol.linkTo = target;
    
    s[link_name] = symbol;
    
}



